#ifndef THREAD_POOL_H
#define THREAD_POOL_H

#include <functional>
#include <thread>
#include <queue>
#include <condition_variable>
#include <atomic>

class ThreadPool
{
private:
    std::queue<std::function<void()>> tasks_;
    std::vector<std::thread> workers_;
    std::condition_variable cv_;
    std::mutex mtx_;
    bool running_;
    std::atomic<int> max_threads_;
    std::atomic<int> active_workers_;

public:
    ThreadPool(int nums) : running_(true), max_threads_(nums), active_workers_(0)
    {
        while (nums--)
        {
            workers_.emplace_back([&]{
                while (running_) {
                    std::unique_lock<std::mutex> lck(mtx_);
                    cv_.wait(lck, [&]{ return !tasks_.empty(); });
                    auto task = tasks_.front();
                    tasks_.pop();
                    lck.unlock();

                    active_workers_.fetch_add(1);
                    task();
                    active_workers_.fetch_sub(1);
                }
            });
            workers_.back().detach();
        }
    }

    ~ThreadPool()
    {
        stop();
    }

    void submit(std::function<void()> task)
    {
        {
            std::unique_lock<std::mutex> lck(mtx_);
            tasks_.push(std::move(task));
        }
        cv_.notify_one();
    }

    bool is_free()
    {
        return active_workers_.load() == 0 && tasks_.empty();
    }

    void stop()
    {
        running_ = false;
        while (active_workers_.load()) {}
    }
};

#endif /* THREAD_POOL_H */
